package com.njcb.ams.web.interceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.njcb.ams.support.exception.ExceptionWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

import com.njcb.ams.pojo.dto.standard.Response;
import com.njcb.ams.support.exception.AppException;
import com.njcb.ams.support.exception.BusinessException;
import com.njcb.ams.support.exception.ExceptionUtil;
import com.njcb.ams.util.AmsJsonUtils;

/**
 * web 异常处理解析器、统一处理系统输出的异常
 *
 * @author LIUYANLONG
 *
 */
public class ExceptionResolver extends ExceptionHandlerExceptionResolver {
	private static final Logger logger = LoggerFactory.getLogger(ExceptionResolver.class);

	private String defaultErrorView;

	public String getDefaultErrorView() {
		return defaultErrorView;
	}

	public void setDefaultErrorView(String defaultErrorView) {
		this.defaultErrorView = defaultErrorView;
	}

	@Override
	protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
		if (handlerMethod == null) {
			return null;
		}
		Method method = handlerMethod.getMethod();
		if (method == null) {
			return null;
		}
		// 如果定义了ExceptionHandler则返回相应的Map中的数据
		ModelAndView returnValue = super.doResolveHandlerMethodException(request, response, handlerMethod, exception);
		ResponseBody responseBodyAnn = AnnotationUtils.findAnnotation(method, ResponseBody.class);
		if (responseBodyAnn != null) {
			try {
				// 如果没有ExceptionHandler注解那么returnValue就为空
				if (returnValue == null) {
					String code = "9999";
					String msg = "系统错误";
					if (exception instanceof AppException) {
						code = ((AppException) exception).getErrorCode();
						msg = ExceptionUtil.getUserErrorText(((AppException) exception).getErrorCode());
						msg = msg + "[" + exception.getMessage() + "]";
					} else if (exception instanceof BusinessException) {
						code = ((BusinessException) exception).getErrorCode();
						msg = ExceptionUtil.getUserErrorText(((BusinessException) exception).getErrorCode());
						msg = msg + "[" + exception.getMessage() + "]";
					} else {
						msg = getEexceptionMessage(exception);
					}
					ExceptionWriter.writeLog(code,exception.getMessage(),exception);
					try {
						ServletOutputStream out = response.getOutputStream();
						response.setContentType("application/json;charset=utf-8");
						response.setHeader("Origin",request.getHeader("Origin"));
						out.write(AmsJsonUtils.objectToJson(Response.buildFail(code, msg)).getBytes("UTF-8"));
						out.flush();
					} catch (IllegalStateException e) {
						ExceptionWriter.writeLog("",exception.getMessage(),exception);
					}
					return new ModelAndView();
				}

				//带ResponseStatus注解的异常直接将状态码和异常原因返回在http头中
				ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(method, ResponseStatus.class);
				if (responseStatusAnn != null) {
					HttpStatus responseStatus = responseStatusAnn.value();
					String reason = responseStatusAnn.reason();
					if (!StringUtils.hasText(reason)) {
						response.setStatus(responseStatus.value());
					} else {
						try {
							response.sendError(responseStatus.value(), reason);
						} catch (IOException e) {
							ExceptionWriter.writeLog("http error",e.getMessage(),e);
						}
					}
				}
				return handleResponseBody(returnValue, request, response);
			} catch (Exception e) {
				ExceptionWriter.writeLog("",e.getMessage(),e);
				ModelAndView mv = new ModelAndView(defaultErrorView);
				mv.addObject("errMsg",getEexceptionMessage(e));
				return mv;
			}
		}else {
			logger.warn("函数{}没有添加ResponseBody注解,跳转错误页面,错误消息{}", handlerMethod.toString(), exception.getMessage());
			ExceptionWriter.writeLog("",exception.getMessage(),exception);
		}
		if (null == returnValue) {
			logger.warn("returnValue is null");
			returnValue = new ModelAndView();
			if (null == returnValue.getViewName()) {
				returnValue.addObject("errMsg",getEexceptionMessage(exception));
				returnValue.setViewName(defaultErrorView);
			}
		}
		return returnValue;
	}

	private String getEexceptionMessage(Throwable exception){
		String errMsg = "";
		if (null != exception.getCause()) {
			if (null != exception.getCause().getCause()) {
				errMsg = exception.getCause().getCause().getClass().getSimpleName() + "[" + exception.getCause().getCause().getMessage() + "]";
			} else {
				errMsg = exception.getCause().getClass().getSimpleName() + "[" + exception.getCause().getMessage() + "]";
			}
		} else {
			errMsg = exception.getClass().getSimpleName() + "[" + exception.getMessage() + "]";
		}
		if(null == errMsg || errMsg.length() == 0){
			errMsg = "未知错误";
		}
		return errMsg;
	}

	@SuppressWarnings({ "unchecked", "rawtypes", "resource" })
	private ModelAndView handleResponseError(Map value, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
		List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
		if (acceptedMediaTypes.isEmpty()) {
			acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
		}
		MediaType.sortByQualityValue(acceptedMediaTypes);
		HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
		Class<?> returnValueType = value.getClass();
		List<HttpMessageConverter<?>> messageConverters = super.getMessageConverters();
		if (messageConverters != null) {
			for (MediaType acceptedMediaType : acceptedMediaTypes) {
				for (HttpMessageConverter messageConverter : messageConverters) {
					if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
						messageConverter.write(value, acceptedMediaType, outputMessage);
						return new ModelAndView();
					}
				}
			}
		}
		logger.warn("Could not find HttpMessageConverter that supports return type [" + returnValueType + "] and " + acceptedMediaTypes);
		return new ModelAndView();
	}

	private ModelAndView handleResponseBody(ModelAndView returnValue, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		return handleResponseError(returnValue.getModelMap(), request, response);
	}

}
